package mdt.figure;

import mdt.action.SetFigShapeAttrAction;
import mdt.action.SetFigTextAttrAction;
import mdt.model.ConnectorAnchor;
import mdt.model.EdgeModel;
import mdt.model.INodeModel;
import mdt.model.ITextEdge;
import org.tigris.gef.base.Selection;
import org.tigris.gef.persistence.pgml.PgmlUtility;
import org.tigris.gef.presentation.Fig;
import org.tigris.gef.presentation.FigEdge;
import org.tigris.gef.presentation.FigGroup;
import org.tigris.gef.presentation.FigNode;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

/**
 * Created by lambert on 2016/6/17.
 */
public abstract class FigShapedEdge extends FigEdge implements KeyListener, MouseListener, FigMDT{

	private static final long serialVersionUID = 1L;
	static final int CONTENT_MIN_WIDTH = 16;
	static final int CONTENT_MIN_HEIGHT = 16;

	public FigShapedEdge(ITextEdge owner) {
		setBetweenNearestPoints(true);
		setOwner(owner);
	}

	public boolean isSelfEdgeEnabled() {
		return false;
	}

	public void paint(Graphics graphicContext) {
		Graphics g = (Graphics) graphicContext;
		getFig().paint(g);
	}

	public void setFig(Fig f) {
		super.setFig(f);
	}

	public boolean intersects(Rectangle r) {
		return getFig().intersects(r);
	}

	public void setBoundsImpl(int x, int y, int w, int h) {
		super.setBoundsImpl(x, y, w, h);
		getFig().setBounds(x, y, w, h);
	}

	@Override
	public Selection makeSelection() {
		return new SelectionShapedEdgeResize(this);
	}

	//int margin = Integer.MAX_VALUE;
	Rectangle lastBounds;
	int left, right;

	@Override
	public void computeRouteImpl() {
		Rectangle bounds = getFig().getBounds();

		if (bounds.getWidth() == 0) return;


		Fig sourcePortFig = getSourcePortFig();
		Fig destPortFig = getDestPortFig();
		Point boundTopLeft = bounds.getLocation();
		Point boundBotRight = new Point(bounds.x + bounds.width, bounds.y + bounds.height);
		Fig leftFig = sourcePortFig.getBounds().x > destPortFig.getBounds().x ? destPortFig : sourcePortFig;
		Point pLeft = leftFig.connectionPoint(boundTopLeft);
		Point pRight = (leftFig == sourcePortFig ? destPortFig : sourcePortFig).connectionPoint(boundBotRight);

		//there is change to bounds, the edge is being dragged, update the distances
		if (lastBounds == null || lastBounds.width != bounds.width) {
			left = pLeft.x - bounds.x;
			right = bounds.x + bounds.width - pRight.x;
			if (bounds.height < CONTENT_MIN_HEIGHT) {
				bounds.height = CONTENT_MIN_HEIGHT;
				setBounds(bounds);
				calcBounds();
			}
		} else if (lastBounds.equals(bounds)) { //there is no change to bounds, could be caused by dragging of Node
			bounds.x = pLeft.x - left;
			bounds.width = left + right + pRight.x - pLeft.x;
			setBounds(bounds);
			calcBounds();
		}
		lastBounds = bounds;
	}

	public Fig getShapeNode() {
		return getFig();
	}

	public Object clone() {
		FigShapedEdge cloned = (FigShapedEdge)super.clone();
		FigNodeEditable fig = (FigNodeEditable) getFig().clone();
		cloned.setFig(fig);

		if (lastBounds != null) {
			cloned.lastBounds = new Rectangle(lastBounds);
		}

		return cloned;
	}

	public void translateCloned(int dx, int dy) {

		if (lastBounds != null)
			lastBounds.translate(0, dy);

		super.translate(0, dy);
	}

	@Override
	public void renderingChanged() {
		damage();
	}

	protected boolean determineFigNodes() {
		Object owner = getOwner();
		if (owner == null) {
			return false;
		}
		if (getLayer() == null) {
			return false;
		}

		EdgeModel sm = (EdgeModel) owner;

		INodeModel newSource = sm.getSrcConnector().getConnectorOwner();
		INodeModel newDest = sm.getDstConnector().getConnectorOwner();

		Fig currentSourceFig = getSourceFigNode();
		Fig currentDestFig = getDestFigNode();
		INodeModel currentSource = null;
		INodeModel currentDestination = null;
		if (currentSourceFig != null && currentDestFig != null) {
			currentSource = (INodeModel) currentSourceFig.getOwner();
			currentDestination = (INodeModel) currentDestFig.getOwner();
		}
		if (newSource != currentSource || newDest != currentDestination) {
			FigNetworkDevice newSourceFig = (FigNetworkDevice) getNoEdgePresentationFor(newSource);
			FigNetworkDevice newDestFig = (FigNetworkDevice) getNoEdgePresentationFor(newDest);
			if (newSourceFig != currentSourceFig) {
				setSourceFigNode((FigNode) newSourceFig);
				setSourcePortFig(newSourceFig.getPortFig(newSource));
			}
			if (newDestFig != currentDestFig) {
				setDestFigNode((FigNode) newDestFig);
				setDestPortFig(newDestFig.getPortFig(newDest));
			}
			((FigNode) newSourceFig).updateEdges();
			if (newDestFig != newSourceFig) {
				((FigNode) newDestFig).updateEdges();
			}
			calcBounds();
		}

		return true;
	}

	private Fig getNoEdgePresentationFor(Object element) {
		if (element == null) {
			throw new IllegalArgumentException("Can't search for a null owner");
		}

		java.util.List contents = PgmlUtility.getContentsNoEdges(getLayer());
		int figCount = contents.size();
		for (int figIndex = 0; figIndex < figCount; ++figIndex) {
			Fig fig = (Fig) contents.get(figIndex);
			if (fig.getOwner() == element) {
				return fig;
			}
		}
		throw new IllegalStateException("Can't find a FigNode representing " + element.getClass().getCanonicalName());
	}

	public void mouseClicked(MouseEvent me) {
		if (me.isConsumed()) {
			return;
		}
		if (me.getClickCount() >= 2 && !(me.isPopupTrigger() || me.getModifiers() == InputEvent.BUTTON3_MASK)
				&& getOwner() != null) {
			Rectangle r = new Rectangle(me.getX() - 2, me.getY() - 2, 4, 4);

			if (getFig() != null && getFig().hit(r)) {
				((FigNodeEditable)getFig()).mouseClicked(me);
				return;
			}
			Fig f = hitFig(r);
			if (f instanceof MouseListener && f.isVisible()) {
				((MouseListener) f).mouseClicked(me);
			} else if (f instanceof FigGroup && f.isVisible()) {
				// this enables direct text editing for sub figs of a
				// FigGroup object:
				Fig f2 = ((FigGroup) f).hitFig(r);
				if (f2 instanceof MouseListener) {
					((MouseListener) f2).mouseClicked(me);
				}
			}
		}
	}

	public void keyPressed(KeyEvent ke) {
		if (ke.isConsumed() || getOwner() == null) {
			return;
		}

		((FigNodeEditable)getFig()).keyPressed(ke);
	}

	public void keyReleased(KeyEvent ke) {
		if (ke.isConsumed() || getOwner() == null) {
			return;
		}

		((FigNodeEditable)getFig()).keyReleased(ke);
	}

	public void keyTyped(KeyEvent ke) {
		if (ke.isConsumed() || getOwner() == null) {
			return;
		}

		((FigNodeEditable)getFig()).keyTyped(ke);
	}

	protected Object createEdgeConnectorModel() {
		return new ConnectorAnchor();
	}

	@Override
	public void mousePressed(MouseEvent e) {
		((FigNodeEditable)getFig()).mousePressed(e);
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		((FigNodeEditable)getFig()).mouseReleased(e);
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		((FigNodeEditable)getFig()).mouseEntered(e);
	}

	@Override
	public void mouseExited(MouseEvent e) {
		((FigNodeEditable)getFig()).mouseExited(e);
	}


	public int getEdgeConnectorPercent(int x, int y) {
		int perimeter = getPerimeterLength();
		Rectangle shapeBounds = getFig().getBounds();
		int bx = shapeBounds.x;
		int by = shapeBounds.y;
		int bw = shapeBounds.width;
		int bh = shapeBounds.height;
		if (y == by ) {
			return 100 * (x - bx) / perimeter;
		} else if (x == bx + bw - 1) {
			return 100 * (bw + y - by) / perimeter;
		} else if (y == by + bh - 1) {
			return 100 * (bw + bh + bx + bw - x) / perimeter;
		} else {
			return 100 * (bw * 2 + bh + by + bh - y) / perimeter;
		}
	}

	public Vector getPopUpActions(MouseEvent me) {
		Vector popUpActions = super.getPopUpActions(me);
		popUpActions.add(new JSeparator());
		popUpActions.add(new JMenuItem(new SetFigShapeAttrAction()));
		popUpActions.add(new JMenuItem(new SetFigTextAttrAction()));
		return popUpActions;
	}

	public void setFilled(boolean f) {
		getFig().setFilled(f);
	}

	public boolean isFilled() {
		return getFig().isFilled();
	}

	public void setFillColor(Color col) {
		getFig().setFillColor(col);
	}

	public Color getFillColor() {
		return getFig().getFillColor();
	}

	public int getTopMargin() {
		return ((FigNodeEditable)getFig()).getFigText().getTopMargin();
	}

	public void setTopMargin(int m) {
		((FigNodeEditable)getFig()).getFigText().setTopMargin(m);
		calcBounds();
	}

	public int getBotMargin() {
		return ((FigNodeEditable)getFig()).getFigText().getBotMargin();
	}

	public void setBotMargin(int m) {
		((FigNodeEditable)getFig()).getFigText().setBotMargin(m);
		calcBounds();
	}

	public int getLeftMargin() {
		return ((FigNodeEditable)getFig()).getFigText().getLeftMargin();
	}

	public void setLeftMargin(int m) {
		((FigNodeEditable)getFig()).getFigText().getLeftMargin();
		calcBounds();
	}

	public int getRightMargin() {
		return ((FigNodeEditable)getFig()).getFigText().getRightMargin();
	}

	public void setRightMargin(int m) {
		((FigNodeEditable)getFig()).getFigText().setRightMargin(m);
		calcBounds();
	}
}
